home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume22 / nn6.4 / part08 < prev    next >
Encoding:
Internet Message Format  |  1990-06-07  |  54.4 KB

  1. Subject:  v22i043:  NN Newsreader, release 6.4, Part08/21
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4. X-Checksum-Snefru: 1aa4e288 878772b0 16cb5b7f fbf16c1f
  5.  
  6. Submitted-by: "Kim F. Storm" <storm@texas.dk>
  7. Posting-number: Volume 22, Issue 43
  8. Archive-name: nn6.4/part08
  9.  
  10. #! /bin/sh
  11. # This is a shell archive.  Remove anything before this line, then feed it
  12. # into a shell via "sh file" or similar.  To overwrite existing files,
  13. # type "sh file -c".
  14. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  15. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  16. # Contents:  man/nnadmin.1m patchlevel.h regexp.c
  17. # Wrapped by storm@texas.dk on Sun May  6 18:19:35 1990
  18. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  19. echo If this archive is complete, you will see the following message:
  20. echo '          "shar: End of archive 8 (of 22)."'
  21. if test -f 'man/nnadmin.1m' -a "${1}" != "-c" ; then 
  22.   echo shar: Will not clobber existing file \"'man/nnadmin.1m'\"
  23. else
  24.   echo shar: Extracting \"'man/nnadmin.1m'\" \(20019 characters\)
  25.   sed "s/^X//" >'man/nnadmin.1m' <<'END_OF_FILE'
  26. X.TH NNADMIN 1M "Release 6.4"
  27. X.\" (c) Copyright 1988, 1990, Kim F. Storm.  All rights reserved.
  28. X.UC 4
  29. X.SH NAME
  30. Xnnadmin \- nn database administration
  31. X.SH SYNOPSIS
  32. X.B nnadmin
  33. X[
  34. X.I commands
  35. X]
  36. X.SH DESCRIPTION
  37. X.I nnadmin
  38. Xis a control program for the \fInnmaster\fP(1M) daemon which is
  39. Xresponsible for building and maintaining the database used by the
  40. X\fInn\fP(1) news reader.
  41. X.LP
  42. X\fInnadmin\fP allows you to display extracts from the log file,
  43. Xdisplay the "raw" contents of the database, make consistency checks on
  44. Xthe database, instruct the running \fInnmaster\fP to expire one or
  45. Xmore groups, alter the options of the running \fInnmaster\fP, and much
  46. Xmore.
  47. X.LP
  48. X\fInnadmin\fP runs in two modes: interactive and non-interactive.
  49. X.LP
  50. XIn interactive mode, simple one line menus are used to show the
  51. Xavailable operations which are then selected by typing the letter
  52. Xassociated with the command (normally the first letter in the command
  53. Xname).
  54. X.LP
  55. XIn non-interactive mode, the
  56. X.I commands
  57. Xargument will be used as a series of key-strokes which are interpreted
  58. Xexactly as if they were typed in from the keyboard in interactive
  59. Xmode.  For example, to stop the \fInnmaster\fP, the following
  60. Xinvokation of nnadmin can be used:
  61. X.br
  62. X    \fInnadmin\fP MK
  63. X.br
  64. Xwhich will select the (M)aster submenu from the main menu, and then
  65. Xthe (K)ill entry from the submenu.
  66. X.LP
  67. XIn non-interactive mode, the menus are not displayed and the commands
  68. Xare not echoed!  \fInnadmin\fP will exit when there are no more
  69. Xkey-strokes to be read from the
  70. X.I commands
  71. Xargument.  It is not possible to specify a group name in the
  72. X.I commands
  73. Xargument, so the functionalities of \fInnadmin\fP that relates to
  74. Xspecific groups are only available in interactive mode.
  75. X.LP
  76. XSome "dangerous" commands will require that you confirm them by
  77. Xfollowing them by "Y" on the command line.  The most noteable are
  78. XIY (initialize database) and EY (expire all groups).  These commands
  79. Xwill be marked with a \fB[Y]\fP following the command name.
  80. X.LP
  81. XYou can also invoke an interactive \fInnadmin\fP using the
  82. X.B :admin
  83. Xcommand in \fInn\fP.
  84. X.SH SHELL ESCAPES
  85. XAt all prompts you can hit `!' to spawn a subshell.
  86. X.LP
  87. XThe working
  88. Xdirectory of the subshell will be changed to the database directory
  89. Xwhen invoked from the MASTER or DUMP menus, and it will changed to the
  90. Xgroup's spool directory (if it exists) when invoked from the GROUP
  91. Xmenu.
  92. X.SH MAIN MENU
  93. XFrom the main menu (identified by the
  94. X.B ADMIN
  95. Xprompt) you can select the following operations:
  96. X.TP
  97. X.B C)onf
  98. X.br
  99. XShow current configuration parameters such as directories, files,
  100. Xprograms, network usage, etc.
  101. X.TP
  102. X\fBE)xpire [Y]\fP
  103. X.br
  104. XSend a request to the \fInnmaster\fP daemon to schedule (and run)
  105. Xexpire for all groups in the database.
  106. X.TP
  107. X.B G)roups
  108. X.br
  109. XEnter the GROUP submenu.
  110. X.TP
  111. X\fBI)nit [Y]\fP
  112. X.br
  113. XSend a request to the \fInnmaster\fP daemon to recollect all
  114. Xgroups in the database.
  115. X.TP
  116. X.B L)og
  117. X.br
  118. XEnter the LOG submenu.
  119. X.TP
  120. X.B M)aster
  121. X.br
  122. XEnter the MASTER submenu.
  123. X.TP
  124. X.B Q)uit
  125. X.br
  126. XQuit \fInnadmin\fP.
  127. X.TP
  128. X.B S)tat
  129. X.br
  130. XPrint general statistics about the database.  See the section on
  131. XDatabase Statistics below.
  132. X.TP
  133. X.B U)pdate
  134. X.br
  135. XUpdate the incore copy of the database master index.
  136. X.TP
  137. X.B V)alidate
  138. X.br
  139. XMake a thorough consistency check on the database.  If inconsistencies
  140. Xare found in a group, you will be asked whether a request should be
  141. Xsent to the \fInnmaster\fP daemon to recollect the group (in
  142. Xnon-interactive mode, requests will be sent automatically for all
  143. Xcorrupted groups).
  144. X.TP
  145. X.B W)akeup
  146. X.br
  147. XSend a wakeup signal to the \fInnmaster\fP daemon to have it receive
  148. Xmessages sent to it, perform the required actions, and then collect
  149. Xarticles as necessary.
  150. X.TP
  151. X\fBZ\fP (silent validation)
  152. X.br
  153. XThis operation is identical to the Validate operation, expect that no
  154. Xoutput is produced during the consistency check; this operation is
  155. Xused by the \fInnmaster\fP to execute the \-\fBC\fP option.
  156. X.SH THE MASTER MENU
  157. XThe master menu (identified by the
  158. X.B MASTER
  159. Xprompt) provides access to overall database information, and to send
  160. Xcontrol messages to the \fInnmaster\fP daemon.
  161. X.TP
  162. X.B D)ump
  163. XEnter the DUMP submenu.
  164. X.TP
  165. X.B F)iles
  166. X.br
  167. XPrint a listing (using
  168. X.IR ls (1))
  169. Xof all the data and index files in the database.
  170. X.TP
  171. X.B G)roup
  172. X.br
  173. XPrint the master index entry for a single group identified by its
  174. Xinternal group number.
  175. X.TP
  176. X.B K)ill
  177. X.br
  178. XStop the \fInnmaster\fP when it has finished its current task.
  179. X.TP
  180. X.B O)ptions
  181. X.br
  182. XChange the runtime options of the running \fInnmaster\fP daemon.
  183. XCurrently, only the value of the \-r and \-e options can be modified.
  184. X.TP
  185. X.B S)tat
  186. X.br
  187. XPrint general statistics about the database.  See the section on
  188. XDatabase Statistics below.
  189. X.TP
  190. X.B T)race
  191. X.br
  192. XTurn the trace option \-t on or off in the running \fInnmaster\fP.
  193. X.SH THE DUMP MENU
  194. XThe dump menu (identified by the
  195. X.B DUMP
  196. Xprompt) allows you to print the master index entry for various
  197. Xselections of groups in the database.
  198. X.TP
  199. X.B A)ll
  200. X.br
  201. XPrint all groups in the database.
  202. X.TP
  203. X.B E)mpty
  204. X.br
  205. XPrint the empty groups in the database.
  206. X.TP
  207. X.B H)oles
  208. XPrint the groups where the `min' field in the active file is not the
  209. Xfirst article saved in the database (because it doesn't exist or
  210. Xbecause it is ignored for some other reason, e.g. bad or old).
  211. X.TP
  212. X.B I)gnored
  213. XPrint groups which are ignored, either in the GROUPS file or because
  214. Xof some other condition (mainly no spool directory).
  215. X.TP
  216. X.B N)on-empty
  217. X.br
  218. XPrint the non-empty groups in the database.
  219. X.TP
  220. X.B V)alid
  221. XPrint the groups which are present in the active file.
  222. X.TP
  223. X.B in(W)alid
  224. XPrint the groups in the database which are not present in the active
  225. Xfile.
  226. X.SH THE LOG MENU
  227. XThe log menu (identified by the
  228. X.B LOG
  229. Xprompt) enables you the extract specific entries from the log file,
  230. Xand to truncate the log file.
  231. X.LP
  232. XThe entries in the log file share the following format:
  233. X.sp 0.5v
  234. X    <class>: <date> <time> (<user>): <message>
  235. X.sp 0.5v
  236. Xwhere <class> identifies the message class, the <date> and <time>
  237. Xspecify when the entry was made, the <user> specifies who created the
  238. Xentry (the letter "M" denote the \fInnmaster\fP), and the <message> is
  239. Xthe text of the entry.
  240. X.LP
  241. XTo extract the log file entries of a specific class, simply enter the
  242. Xletter identifying the class:
  243. X.TP
  244. X.B A - admin to master communication
  245. X.br
  246. XThis class of messages are related to the sending of messages from an
  247. X\fInnadmin\fP program to the \fInnmaster\fP daemon.
  248. X.TP
  249. X.B B - bad articles
  250. XReports about bad articles which have been ignored or removed
  251. X(controlled by the \-\fBb\fP and \-\fBB\fP options to \fInnmaster\fP).
  252. X.TP
  253. X.B C - collection statistics
  254. X.br
  255. XStatistics about collection of new articles.  The message has the format:
  256. X.br
  257. X    Collect: \fInnn\fP art, \fIppp\fP gr, \fIttt\fP s
  258. X.br
  259. Xmeaning that
  260. X.I nnn
  261. Xarticles in
  262. X.I ppp
  263. Xgroups were collected in
  264. X.I ttt
  265. Xseconds (real time).
  266. X.TP
  267. X.B E - fatal errors
  268. X.br
  269. XFatal errors encountered during operation.  These errors require
  270. Xmanual intervention to be fixed (some of the fatal errors occur if
  271. Xthing that "cannot happen" happens anyway, and may indicate a bug
  272. Xin the software).
  273. X.TP
  274. X.B M - nnmaster messages.
  275. X.br
  276. XMaster start/stop messages.
  277. X.TP
  278. X.B N - NNTP related messages
  279. X.br
  280. XVarious messages related to the NNTP part of the nnmaster, mostly
  281. Xabout lost connections and failed attempts to connect to the NNTP
  282. Xserver.  These messages should only appear if you use NNTP, and your
  283. XNNTP server is down for some reason.
  284. X.TP
  285. X.B O - old articles
  286. XReports related to ignoring (and removing) old articles when building
  287. Xthe database (controlled by the \-\fBO\fP and \-\fBB\fP options to
  288. X\fInnmaster\fP).
  289. X.TP
  290. X.B R - reports
  291. X.br
  292. XNon-fatal error which enables the \fInnmaster\fP to continue
  293. Xoperation, but may prevent a user to run \fInn\fP (file access
  294. Xproblems).  Reported problems should be checked.  The most common
  295. Xreport message will probably be
  296. X.br
  297. X    some.group: no directory
  298. X.br
  299. Xwhich indicates that the spool directory for that group has
  300. Xdisappeared (most likely because it has been rmgroup'ed).
  301. X.TP
  302. X.B T - trace output
  303. X.br
  304. XMessages produced as a result of using the \-t option on the
  305. X\fInnmaster\fP.  This is primarily for debugging purposes.
  306. X.TP
  307. X.B U - usage statistics
  308. X.br
  309. XIf \fInn\fP is compiled with the STATISTICS option enabled, an entry
  310. Xwill be made in the log file every time a user has spent more than
  311. Xfive minutes on news reading.  The message will have the following format:
  312. X.br
  313. X    USAGE \fIhours.minutes\fP
  314. X.br
  315. XSince it is possible to
  316. Xsuspend
  317. X\fInn\fP, or leave the terminal while \fInn\fP is active, \fInn\fP
  318. Xtries to be intelligent when it calculates the usage time so it will
  319. Xreflect the actual time spent on news reading.  The usage statistics
  320. Xcan be summarized using the \fInnusage\fP(1M) program.
  321. X.TP
  322. X.B V - validation errors
  323. X.br
  324. XWhen inconsistencies are detected in the database during validation,
  325. Xan entry for each corrupted group will be entered in the log file.
  326. X.TP
  327. X.B X - expire statistics
  328. X.br
  329. XMessages similar to the Collect statistics reporting the result of
  330. Xrunning expire on the database.  Reports related to ignoring, removing,
  331. Xrenumbering, and reactivation of groups are also given class X.
  332. X.LP
  333. XTo extract a specific entry class,
  334. X.IR grep (1)
  335. Xis used, so it may take a while on a large log file.
  336. X.LP
  337. XThere are also a few special operations on the log file:
  338. X.TP
  339. X.B G)roup
  340. X.br
  341. XExtract the entries which refers to a specified group.
  342. X.TP
  343. X.B (1-9) tail
  344. X.br
  345. XInvoke
  346. X.IR tail (1)
  347. Xto extract the last 10-90 entries in the log file.
  348. X.TP
  349. X\fBspace\fP
  350. X.br
  351. XEquivalent to \fB1\fP (list last 10 lines of log).
  352. X.TP
  353. X.B (.) all
  354. X.br
  355. XDisplay the complete log file.
  356. X.TP
  357. X.B (@) clean [Y]
  358. X.br
  359. XMove the Log file to Log.old, and create a new empty Log file.  If you
  360. Xwant to clean out the old log file as well, simply repeat the clean
  361. Xoperation (this will result in an empty Log.old file.)
  362. X.SH THE GROUP MENU
  363. XWhen you enter the group menu (identified by the
  364. X.B GROUP
  365. Xprompt), \fInnadmin\fP will prompt you for the name of a news group,
  366. Xwhich you can enter with the usual completion feature described in the
  367. X\fInn\fP(1) manual.  You can then perform the following operations on
  368. Xthe specified group:
  369. X.TP
  370. X.B C)lear_flag
  371. X.br
  372. XClear a group specific flag.  See the section on group flags below.
  373. X.TP
  374. X.B D)ata
  375. X.br
  376. XDump the contents of the data file containing the extracted article
  377. Xheaders for the group.
  378. X.TP
  379. X.B E)xpire
  380. X.br
  381. XRequest the \fInnmaster\fP to run expire on the group.
  382. X.TP
  383. X.B F)iles
  384. X.br
  385. XList the files (using
  386. X.IR ls (1))
  387. Xcontaining the index and data for the group.
  388. X.TP
  389. X.B G)roup
  390. X.br
  391. XSwitch to another group.
  392. X.TP
  393. X.B H)eader
  394. X.br
  395. XDump the master index entry for the group.
  396. X.TP
  397. X.B R)ecollect
  398. X.br
  399. XRequest the \fInnmaster\fP to recollect all articles in the group.
  400. X.TP
  401. X.B S)et_flag
  402. X.br
  403. XSet a group specific flag.  See the section on group flags below.
  404. X.TP
  405. X.B V)alidate
  406. X.br
  407. XPerform validation on the group's database information.
  408. X.TP
  409. X.B Z)ap [Y]
  410. X.br
  411. XRemove group from news system - this will be done by running the
  412. X\fIrmgroup\fP program which must reside in the NEWS_LIB directory.
  413. XOf course, this should be done with great caution.
  414. X.SH INDIVIDUAL GROUP FLAGS
  415. XYou can set and clear the following flags for individual groups to
  416. Xcontrol the future behaviour of \fInnmaster\fP on that group.
  417. X.LP
  418. XNotice that these flags will be reset to their default value if you
  419. Xreinitialize the database using \fInnmaster\fP \-I.  To change these
  420. Xflags permanently, they should be set or cleared in the GROUPS file.
  421. X.TP
  422. X.B A)lways_digest
  423. X.br
  424. XNormally, \fInnmaster\fP will only attempt to split digests into
  425. Xindividual articles if it can easily recognize an article as a digest.
  426. XThis requires that the word "digest" appears somewhere in the subject
  427. Xline, and that one of the first few lines in the body of the article
  428. Xloosely matches the subject.  A few news groups frequently receives
  429. Xdigests which break one or both of these requirements.  To have
  430. X\fInnmaster\fP split these digests into individual articles anyway,
  431. Xyou can turn on the "always digest" flag on these news groups.
  432. XThis will instruct \fInnmaster\fP to treat
  433. X.I all
  434. Xarticles in the group as digests (naturally, articles which are then
  435. Xfound not to contain other articles are still treated as normal articles.)
  436. X.TP
  437. X.B C)ontrol
  438. X.br
  439. XThis is a special flag for the control group.  It indicates that the
  440. X"Newsgroups:" field in the article header cannot be trusted (it does
  441. Xnot specify the groups to which the article has been posted.)
  442. X.TP
  443. X.B D)irectory missing
  444. X.br
  445. XThis flag indicates that the spool directory for the news group cannot
  446. Xbe found (the group has probably been removed with
  447. X.IR rmgroup (1M)).
  448. XIt is set automatically be the \fInnmaster\fP if it cannot
  449. Xaccess the directory.  When the flag is set, \fInnmaster\fP completely
  450. Xignores the group, so it can be used to disable news collection in
  451. Xspecific groups.  If you recreate the group or the directory
  452. Xmanually, you must also clear this flag to have the \fInnmaster\fP
  453. Xrecognize the group again.
  454. X.TP
  455. X.B M)oderated
  456. X.br
  457. XIndicates that the group is moderated.  This flag is normally
  458. Xinitialized automatically from the active file, and it should not be
  459. Xchanged lightly.
  460. X.TP
  461. X.B N)ever_digest
  462. X.br
  463. XThis is the opposite of the "always digest" flag; when set, the
  464. X\fInnmaster\fP will never attempt to split any articles in that group
  465. Xinto subarticles.
  466. X.SH DATABASE STATISTICS DISPLAY
  467. XWhen you select the (S)tat operation in the main or master menus, you
  468. Xwill get some general statistics about the database:
  469. X.TP
  470. Xinitialized
  471. X.br
  472. XThe time when the database was last rebuild using \fInnmaster\fP -I.
  473. X.TP
  474. Xlast_scan, last_size
  475. X.br
  476. XThe time stamp on the active file and its size the last time the
  477. X\fInnmaster\fP read it.
  478. X.TP
  479. Xno of groups
  480. X.br
  481. XThe total number of groups in the database.
  482. X.TP
  483. XArticles
  484. X.br
  485. XThe total number of articles in all groups.  This is not an
  486. Xexact number, because it will count split digests as a single article
  487. X(making the number too small), and it may count some articles that
  488. Xhave been expired (making the number too large).
  489. X.TP
  490. XDisk usage
  491. X.br
  492. XThe total number of (1 kbyte) disk blocks occupied by the database.
  493. X.SH MASTER INDEX ENTRIES
  494. XThe master index entries displayed when you select the (H)eader
  495. Xoperation in the master and group menus contain the following information:
  496. X.TP
  497. X\fIgroup_name  group_number\fP
  498. X.br
  499. XThe first line of the display will show the name of the group and the
  500. Xinternal group number which is used to identify the group in the database.
  501. X.TP
  502. Xfirst/last art
  503. X.br
  504. XThis is the numbers of the first and last article that are currently
  505. Xstored in the database.
  506. X.TP
  507. Xactive info
  508. X.br
  509. XThis is the numbers of the first and last article in the news system
  510. Xas read from the active file.  They will normally match the numbers
  511. Xabove, but they may differ while the \fInnmaster\fP is working on the
  512. Xgroup (or it has not yet collected all the articles in the group).
  513. X.TP
  514. XOffsets: index->..., data->...
  515. X.br
  516. XThese values show the starting position for the next write operation
  517. Xon the index and data files.  They are primarily used for consistency
  518. Xchecking and recovery after a system crash, but after an "expire by
  519. Xrewrite" operation (expire method 2) which is performed "in-situ", the
  520. Xdata and index files may physically be longer than the actual data
  521. Xstored in them.
  522. X.TP
  523. XFlags:
  524. X.br
  525. XThis shows the current flags set for this group.  If no flags are set,
  526. Xthe field is omitted from the display.  One extra flag which was not
  527. Xexplained above is the BLOCKED flag; it is a temporary locking flag
  528. Xset on a group by the \fInnmaster\fP while it is updating the database
  529. Xfiles for that group to prevent \fInn\fP clients to access that group.
  530. X.SH RAW DATABASE DISPLAY
  531. XWhen you select the (D)ata operation on the group menu, you will get a
  532. Xcombined display of the raw data and index files for that group.  The
  533. Xindex file contains a single 32 bit value for each existing article
  534. Xnumber.  This value is an offset into the data file pointing to the
  535. Xheader for the corresponding article.
  536. X.LP
  537. XWhen \fInn\fP want to access the article from number N to the last
  538. Xarticle, it looks up the offset for article number N in the index
  539. Xfile, and uses this as the starting point for reading article header
  540. Xinformation in the data file.  It then simply reads to the end of the
  541. Xdata file in which the article headers for articles number N+1, N+2,
  542. Xand so on follows immediately after the header for article number N.
  543. X.LP
  544. XThe article header information is presented in a very terse form; each
  545. Xof the output lines are described below for reference purposes:
  546. X.TP
  547. Xoffset = \fIxxxx\fP    , article # = \fInnnnn\fP   (type)
  548. X.br
  549. XThis shows the offset into the data file and the article number.  The
  550. Xoffset is stored in the index file for quick access.  If no \fItype\fP
  551. Xis printed it is a normal article.  Other types are: "digest header"
  552. Xand "digest sub-article".
  553. X.TP
  554. Xxpost(\fIcount\fP):  \fInnn\fP, \fInnn\fP, \fInnn\fP, ...
  555. X.br
  556. XCross-postings to other groups are encoded as a list of internal group
  557. Xnumbers.
  558. X.TP
  559. Xts=\fInn\fP hp=\fInn\fP fp=\fInn\fP lp=\fInn\fP ref=\fInn\fP[+Re] lines=\fInn\fP
  560. X.br
  561. XThese values are used by \fInn\fP to sort, present, and access an
  562. Xarticle:
  563. X.br
  564. X.B ts
  565. Xis the
  566. X.I time stamp
  567. Xon the article; it is a simple encoding of the posting date and time
  568. Xfound in the Date: field.
  569. X.br
  570. X.BR hp ,
  571. X.BR fp ,
  572. Xand
  573. X.B lp
  574. Xare offsets into the file containing the article text: the \fIheader
  575. Xposition\fP, \fIfirst text position\fP, and \fIlast text position\fP.
  576. XThe first will be zero for normal articles, but not for articles in a
  577. Xsplit digest.  The last will be equal to the length of the file for
  578. Xnormal articles, but not inside digests.
  579. X.br
  580. X.B ref
  581. Xis the number of references on the Reference: line.  If "+Re" follows
  582. Xthe number, the subject line contained a "Re:" prefix which has been
  583. Xremoved.
  584. X.TP
  585. XSender(\fIlength\fP): \fIname\fP
  586. X.br
  587. XThe name of the sender in "ready to print" format, i.e. reduced to 16
  588. Xcharacters as explained in the \fInn\fP manual.
  589. X.TP
  590. XSubj(\fIlength\fP): \fIsubject\fP
  591. X.br
  592. XThis is the full subject line from the article header (except for Re:
  593. Xprefixes in various formats).
  594. X.fi
  595. X.SH FILES
  596. XThe $db, $lib, and $news used below are synonyms for the DB_DIRECTORY,
  597. XLIB_DIRECTORY, and the news system's lib directories respectively.
  598. X.br
  599. X.DT
  600. X.ta \w'$db/DATA/\fInnn\fP.dx'u+3m
  601. X.\"ta 0 16
  602. X$db/MASTER    Database master index
  603. X.br
  604. X$db/GROUPS    News group names in MASTER file order
  605. X.br
  606. X$db/DATA/\fInnn\fP.x    Index file for group number \fInnn\fP
  607. X.br
  608. X$db/DATA/\fInnn\fP.d    Data file for group number \fInnn\fP
  609. X.br
  610. X$master/GATE    Message channel from \fInnadmin\fP to \fInnmaster\fP
  611. X.br
  612. X$master/MPID    The process id of the \fInnmaster\fP daemon.
  613. X.br
  614. X$Log    The log file (truncate it regularly!)
  615. X.DT
  616. X.LP
  617. XThe MASTER file contains a record for each news group, occurring in
  618. Xthe same sequence as the group names in the GROUPS file.  The sequence
  619. Xalso defines the group numbers used to identify the files in the
  620. Xdatabase and in a few other places.
  621. X.LP
  622. XThe GATE file will be created by \fInnadmin\fP when needed, and
  623. Xremoved by \fInnmaster\fP when it has read it.  Therefore, to send a
  624. Xmessage to the \fInnmaster\fP requires that you are allowed to write
  625. Xin the $master directory.
  626. X.SH SEE ALSO
  627. Xnn(1), nncheck(1), nngrep(1), nntidy(1)
  628. X.br
  629. Xnnquery(1M), nnusage(1M), nnmaster(8)
  630. X.SH WARNINGS
  631. XThe GATE file is created with the owner and modes of the user that
  632. Xruns \fInnadmin\fP which may cause problems if the owner of the
  633. X\fInnmaster\fP process (normally "news") is not allowed to read the
  634. Xcreated GATE file (a "umask" of 022 is ok.)  Unless you allow ordinary
  635. Xusers to create files in the LIB directory where the GATE file
  636. Xresides, only the owner of the directory (normally "news") and "root"
  637. Xcan use \fInnadmin\fP to send messages to the \fInnmaster\fP.
  638. XHowever, to send a wakeup signal to the master, anybody can run
  639. X.br
  640. X    \fInnmaster\fP -w
  641. X.SH BUGS
  642. XThe user interface is completely out of line with the rest of the
  643. X\fInn\fP family, and the way to run \fInnadmin\fP in the
  644. Xnon-interactive mode is a bit bizarre.  This is not likely to change,
  645. Xbecause I believe there are more important things to do!
  646. X.SH AUTHOR
  647. XKim F. Storm, Texas Instruments A/S, Denmark
  648. X.br
  649. XE-mail: storm@texas.dk
  650. END_OF_FILE
  651.   if test 20019 -ne `wc -c <'man/nnadmin.1m'`; then
  652.     echo shar: \"'man/nnadmin.1m'\" unpacked with wrong size!
  653.   fi
  654.   # end of 'man/nnadmin.1m'
  655. fi
  656. if test -f 'patchlevel.h' -a "${1}" != "-c" ; then 
  657.   echo shar: Will not clobber existing file \"'patchlevel.h'\"
  658. else
  659.   echo shar: Extracting \"'patchlevel.h'\" \(411 characters\)
  660.   sed "s/^X//" >'patchlevel.h' <<'END_OF_FILE'
  661. X/*
  662. X *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  663. X *
  664. X *    Modification history:
  665. X *
  666. X *    1988-07-20: Release 6.0beta     (Denmark)
  667. X *    1988-11-01: Release 6.1     (Europe)
  668. X *    1989-03-21: Release 6.2beta    (FTP)
  669. X *    1989-05-30: Release 6.3        (comp.sources.unix)
  670. X *    1989-09-08: Release 6.3.7    (FTP)
  671. X *    1990-03-03: Release 6.4beta    (FTP)
  672. X *    1990-05-07: Release 6.4        (comp.sources.unix)
  673. X */
  674. X
  675. X#define PATCHLEVEL 0
  676. X
  677. END_OF_FILE
  678.   if test 411 -ne `wc -c <'patchlevel.h'`; then
  679.     echo shar: \"'patchlevel.h'\" unpacked with wrong size!
  680.   fi
  681.   # end of 'patchlevel.h'
  682. fi
  683. if test -f 'regexp.c' -a "${1}" != "-c" ; then 
  684.   echo shar: Will not clobber existing file \"'regexp.c'\"
  685. else
  686.   echo shar: Extracting \"'regexp.c'\" \(30796 characters\)
  687.   sed "s/^X//" >'regexp.c' <<'END_OF_FILE'
  688. X/*
  689. X * regexp.c - regular expression matching
  690. X *
  691. X * NOTICE: THIS CODE HAS BEEN MODIFIED TO FIT THE NN ENVIRONMENT.
  692. X *
  693. X * DESCRIPTION
  694. X *
  695. X *    This source was taken from the pax posting in comp.sources.unix.
  696. X *
  697. X *    Underneath the reformatting and comment blocks which were added to
  698. X *    make it consistent with the rest of the code, you will find a
  699. X *    modified version of Henry Specer's regular expression library.
  700. X *    Henry's functions were modified to provide the minimal regular
  701. X *    expression matching, as required by P1003.  Henry's code was
  702. X *    copyrighted, and copy of the copyright message and restrictions
  703. X *    are provided, verbatim, below:
  704. X *
  705. X *    Copyright (c) 1986 by University of Toronto.
  706. X *    Written by Henry Spencer.  Not derived from licensed software.
  707. X *
  708. X *    Permission is granted to anyone to use this software for any
  709. X *    purpose on any computer system, and to redistribute it freely,
  710. X *    subject to the following restrictions:
  711. X *
  712. X *    1. The author is not responsible for the consequences of use of
  713. X *         this software, no matter how awful, even if they arise
  714. X *       from defects in it.
  715. X *
  716. X *    2. The origin of this software must not be misrepresented, either
  717. X *       by explicit claim or by omission.
  718. X *
  719. X *    3. Altered versions must be plainly marked as such, and must not
  720. X *       be misrepresented as being the original software.
  721. X *
  722. X *     Beware that some of this code is subtly aware of the way operator
  723. X *     precedence is structured in regular expressions.  Serious changes in
  724. X *     regular-expression syntax might require a total rethink.
  725. X *
  726. X * AUTHORS
  727. X *
  728. X *     Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
  729. X *     Henry Spencer, University of Torronto (henry@utzoo.edu)
  730. X *
  731. X * Sponsored by The USENIX Association for public distribution.
  732. X *
  733. X * $Log:    regexp.c,v $
  734. X * Revision 1.1  88/12/23  18:02:32  mark
  735. X * Initial revision
  736. X *
  737. X */
  738. X
  739. X#define NN
  740. X
  741. X/* Headers */
  742. X
  743. X#ifdef NN
  744. X#include "config.h"
  745. X#include "regexp.h"
  746. X#else
  747. X#include "pax.h"
  748. X
  749. X#ifndef lint
  750. Xstatic char    *Ident = "$Id: regexp.c,v 1.1 88/12/23 18:02:32 mark Rel $";
  751. X#endif
  752. X#endif
  753. X
  754. X/*
  755. X * The "internal use only" fields in regexp.h are present to pass info from
  756. X * compile to execute that permits the execute phase to run lots faster on
  757. X * simple cases.  They are:
  758. X *
  759. X * regstart    char that must begin a match; '\0' if none obvious
  760. X * reganch    is the match anchored (at beginning-of-line only)?
  761. X * regmust    string (pointer into program) that match must include, or NULL
  762. X * regmlen    length of regmust string
  763. X *
  764. X * Regstart and reganch permit very fast decisions on suitable starting points
  765. X * for a match, cutting down the work a lot.  Regmust permits fast rejection
  766. X * of lines that cannot possibly match.  The regmust tests are costly enough
  767. X * that regcomp() supplies a regmust only if the r.e. contains something
  768. X * potentially expensive (at present, the only such thing detected is * or +
  769. X * at the start of the r.e., which can involve a lot of backup).  Regmlen is
  770. X * supplied because the test in regexec() needs it and regcomp() is computing
  771. X * it anyway.
  772. X */
  773. X
  774. X/*
  775. X * Structure for regexp "program".  This is essentially a linear encoding
  776. X * of a nondeterministic finite-state machine (aka syntax charts or
  777. X * "railroad normal form" in parsing technology).  Each node is an opcode
  778. X * plus a "nxt" pointer, possibly plus an operand.  "Nxt" pointers of
  779. X * all nodes except BRANCH implement concatenation; a "nxt" pointer with
  780. X * a BRANCH on both ends of it is connecting two alternatives.  (Here we
  781. X * have one of the subtle syntax dependencies:  an individual BRANCH (as
  782. X * opposed to a collection of them) is never concatenated with anything
  783. X * because of operator precedence.)  The operand of some types of node is
  784. X * a literal string; for others, it is a node leading into a sub-FSM.  In
  785. X * particular, the operand of a BRANCH node is the first node of the branch.
  786. X * (NB this is *not* a tree structure:  the tail of the branch connects
  787. X * to the thing following the set of BRANCHes.)  The opcodes are:
  788. X */
  789. X
  790. X/* definition    number    opnd?    meaning */
  791. X#define    END    0        /* no    End of program. */
  792. X#define    BOL    1        /* no    Match "" at beginning of line. */
  793. X#define    EOL    2        /* no    Match "" at end of line. */
  794. X#define    ANY    3        /* no    Match any one character. */
  795. X#define    ANYOF    4        /* str    Match any character in this string. */
  796. X#define    ANYBUT    5        /* str    Match any character not in this
  797. X                 * string. */
  798. X#define    BRANCH    6        /* node    Match this alternative, or the
  799. X                 * nxt... */
  800. X#define    BACK    7        /* no    Match "", "nxt" ptr points backward. */
  801. X#define    EXACTLY    8        /* str    Match this string. */
  802. X#define    NOTHING    9        /* no    Match empty string. */
  803. X#define    STAR    10        /* node    Match this (simple) thing 0 or more
  804. X                 * times. */
  805. X#define    OPEN    20        /* no    Mark this point in input as start of
  806. X                 * #n. */
  807. X /* OPEN+1 is number 1, etc. */
  808. X#define    CLOSE    30        /* no    Analogous to OPEN. */
  809. X
  810. X/*
  811. X * Opcode notes:
  812. X *
  813. X * BRANCH    The set of branches constituting a single choice are hooked
  814. X *        together with their "nxt" pointers, since precedence prevents
  815. X *        anything being concatenated to any individual branch.  The
  816. X *        "nxt" pointer of the last BRANCH in a choice points to the
  817. X *        thing following the whole choice.  This is also where the
  818. X *        final "nxt" pointer of each individual branch points; each
  819. X *        branch starts with the operand node of a BRANCH node.
  820. X *
  821. X * BACK        Normal "nxt" pointers all implicitly point forward; BACK
  822. X *        exists to make loop structures possible.
  823. X *
  824. X * STAR        complex '*', are implemented as circular BRANCH structures
  825. X *        using BACK.  Simple cases (one character per match) are
  826. X *        implemented with STAR for speed and to minimize recursive
  827. X *        plunges.
  828. X *
  829. X * OPEN,CLOSE    ...are numbered at compile time.
  830. X */
  831. X
  832. X/*
  833. X * A node is one char of opcode followed by two chars of "nxt" pointer.
  834. X * "Nxt" pointers are stored as two 8-bit pieces, high order first.  The
  835. X * value is a positive offset from the opcode of the node containing it.
  836. X * An operand, if any, simply follows the node.  (Note that much of the
  837. X * code generation knows about this implicit relationship.)
  838. X *
  839. X * Using two bytes for the "nxt" pointer is vast overkill for most things,
  840. X * but allows patterns to get big without disasters.
  841. X */
  842. X#define    OP(p)    (*(p))
  843. X#define    NEXT(p)    (((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
  844. X#define    OPERAND(p)    ((p) + 3)
  845. X
  846. X/*
  847. X * Utility definitions.
  848. X */
  849. X
  850. X#define    FAIL(m)    { regerror(m); return(NULL); }
  851. X#define    ISMULT(c)    ((c) == '*')
  852. X#define    META    "^$.[()|*\\"
  853. X#ifndef CHARBITS
  854. X#define    UCHARAT(p)    ((int)*(unsigned char *)(p))
  855. X#else
  856. X#define    UCHARAT(p)    ((int)*(p)&CHARBITS)
  857. X#endif
  858. X
  859. X/*
  860. X * Flags to be passed up and down.
  861. X */
  862. X#define    HASWIDTH    01    /* Known never to match null string. */
  863. X#define    SIMPLE        02    /* Simple enough to be STAR operand. */
  864. X#define    SPSTART        04    /* Starts with * */
  865. X#define    WORST        0    /* Worst case. */
  866. X
  867. X/*
  868. X * Global work variables for regcomp().
  869. X */
  870. Xstatic char    *regparse;    /* Input-scan pointer. */
  871. Xstatic int      regnpar;    /* () count. */
  872. Xstatic char     regdummy;
  873. Xstatic char    *regcode;    /* Code-emit pointer; ®dummy = don't. */
  874. Xstatic long     regsize;    /* Code size. */
  875. X
  876. X/*
  877. X * Forward declarations for regcomp()'s friends.
  878. X */
  879. X#ifndef STATIC
  880. X#define    STATIC    static
  881. X#endif
  882. XSTATIC char    *reg();
  883. XSTATIC char    *regbranch();
  884. XSTATIC char    *regpiece();
  885. XSTATIC char    *regatom();
  886. XSTATIC char    *regnode();
  887. XSTATIC char    *regnext();
  888. XSTATIC void     regc();
  889. XSTATIC void     reginsert();
  890. XSTATIC void     regtail();
  891. XSTATIC void     regoptail();
  892. X#ifdef STRCSPN
  893. XSTATIC int      strcspn();
  894. X#endif
  895. X
  896. X/*
  897. X - regcomp - compile a regular expression into internal code
  898. X *
  899. X * We can't allocate space until we know how big the compiled form will be,
  900. X * but we can't compile it (and thus know how big it is) until we've got a
  901. X * place to put the code.  So we cheat:  we compile it twice, once with code
  902. X * generation turned off and size counting turned on, and once "for real".
  903. X * This also means that we don't allocate space until we are sure that the
  904. X * thing really will compile successfully, and we never have to move the
  905. X * code and thus invalidate pointers into it.  (Note that it has to be in
  906. X * one piece because free() must be able to free it all.)
  907. X *
  908. X * Beware that the optimization-preparation code in here knows about some
  909. X * of the structure of the compiled regexp.
  910. X */
  911. Xregexp *regcomp(exp)
  912. Xchar           *exp;
  913. X{
  914. X    register regexp *r;
  915. X    register char  *scan;
  916. X    register char  *longest;
  917. X    register int    len;
  918. X    int             flags;
  919. X    extern char    *malloc();
  920. X
  921. X    if (exp == NULL)
  922. X    FAIL("NULL argument");
  923. X
  924. X    /* First pass: determine size, legality. */
  925. X    regparse = exp;
  926. X    regnpar = 1;
  927. X    regsize = 0L;
  928. X    regcode = ®dummy;
  929. X    regc(MAGIC);
  930. X    if (reg(0, &flags) == NULL)
  931. X    return (NULL);
  932. X
  933. X    /* Small enough for pointer-storage convention? */
  934. X    if (regsize >= 32767L)    /* Probably could be 65535L. */
  935. X    FAIL("regexp too big");
  936. X
  937. X    /* Allocate space. */
  938. X    r = (regexp *) malloc(sizeof(regexp) + (unsigned) regsize);
  939. X    if (r == NULL)
  940. X    FAIL("out of space");
  941. X
  942. X    /* Second pass: emit code. */
  943. X    regparse = exp;
  944. X    regnpar = 1;
  945. X    regcode = r->program;
  946. X    regc(MAGIC);
  947. X    if (reg(0, &flags) == NULL)
  948. X    return (NULL);
  949. X
  950. X    /* Dig out information for optimizations. */
  951. X    r->regstart = '\0';        /* Worst-case defaults. */
  952. X    r->reganch = 0;
  953. X    r->regmust = NULL;
  954. X    r->regmlen = 0;
  955. X    scan = r->program + 1;    /* First BRANCH. */
  956. X    if (OP(regnext(scan)) == END) {    /* Only one top-level choice. */
  957. X    scan = OPERAND(scan);
  958. X
  959. X    /* Starting-point info. */
  960. X    if (OP(scan) == EXACTLY)
  961. X        r->regstart = *OPERAND(scan);
  962. X    else if (OP(scan) == BOL)
  963. X        r->reganch++;
  964. X
  965. X    /*
  966. X     * If there's something expensive in the r.e., find the longest
  967. X     * literal string that must appear and make it the regmust.  Resolve
  968. X     * ties in favor of later strings, since the regstart check works
  969. X     * with the beginning of the r.e. and avoiding duplication
  970. X     * strengthens checking.  Not a strong reason, but sufficient in the
  971. X     * absence of others.
  972. X     */
  973. X    if (flags & SPSTART) {
  974. X        longest = NULL;
  975. X        len = 0;
  976. X        for (; scan != NULL; scan = regnext(scan))
  977. X        if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
  978. X            longest = OPERAND(scan);
  979. X            len = strlen(OPERAND(scan));
  980. X        }
  981. X        r->regmust = longest;
  982. X        r->regmlen = len;
  983. X    }
  984. X    }
  985. X    return (r);
  986. X}
  987. X
  988. X/*
  989. X - reg - regular expression, i.e. main body or parenthesized thing
  990. X *
  991. X * Caller must absorb opening parenthesis.
  992. X *
  993. X * Combining parenthesis handling with the base level of regular expression
  994. X * is a trifle forced, but the need to tie the tails of the branches to what
  995. X * follows makes it hard to avoid.
  996. X */
  997. Xstatic char *reg(paren, flagp)
  998. Xint             paren;        /* Parenthesized? */
  999. Xint            *flagp;
  1000. X{
  1001. X    register char  *ret;
  1002. X    register char  *br;
  1003. X    register char  *ender;
  1004. X    register int    parno;
  1005. X    int             flags;
  1006. X
  1007. X    *flagp = HASWIDTH;        /* Tentatively. */
  1008. X
  1009. X    /* Make an OPEN node, if parenthesized. */
  1010. X    if (paren) {
  1011. X    if (regnpar >= NSUBEXP)
  1012. X        FAIL("too many ()");
  1013. X    parno = regnpar;
  1014. X    regnpar++;
  1015. X    ret = regnode(OPEN + parno);
  1016. X    } else
  1017. X    ret = NULL;
  1018. X
  1019. X    /* Pick up the branches, linking them together. */
  1020. X    br = regbranch(&flags);
  1021. X    if (br == NULL)
  1022. X    return (NULL);
  1023. X    if (ret != NULL)
  1024. X    regtail(ret, br);    /* OPEN -> first. */
  1025. X    else
  1026. X    ret = br;
  1027. X    if (!(flags & HASWIDTH))
  1028. X    *flagp &= ~HASWIDTH;
  1029. X    *flagp |= flags & SPSTART;
  1030. X    while (*regparse == '|') {
  1031. X    regparse++;
  1032. X    br = regbranch(&flags);
  1033. X    if (br == NULL)
  1034. X        return (NULL);
  1035. X    regtail(ret, br);    /* BRANCH -> BRANCH. */
  1036. X    if (!(flags & HASWIDTH))
  1037. X        *flagp &= ~HASWIDTH;
  1038. X    *flagp |= flags & SPSTART;
  1039. X    }
  1040. X
  1041. X    /* Make a closing node, and hook it on the end. */
  1042. X    ender = regnode((paren) ? CLOSE + parno : END);
  1043. X    regtail(ret, ender);
  1044. X
  1045. X    /* Hook the tails of the branches to the closing node. */
  1046. X    for (br = ret; br != NULL; br = regnext(br))
  1047. X    regoptail(br, ender);
  1048. X
  1049. X    /* Check for proper termination. */
  1050. X    if (paren && *regparse++ != ')') {
  1051. X    FAIL("unmatched ()");
  1052. X    } else if (!paren && *regparse != '\0') {
  1053. X    if (*regparse == ')') {
  1054. X        FAIL("unmatched ()");
  1055. X    } else
  1056. X        FAIL("junk on end");/* "Can't happen". */
  1057. X    /* NOTREACHED */
  1058. X    }
  1059. X    return (ret);
  1060. X}
  1061. X
  1062. X/*
  1063. X - regbranch - one alternative of an | operator
  1064. X *
  1065. X * Implements the concatenation operator.
  1066. X */
  1067. Xstatic char  *regbranch(flagp)
  1068. Xint            *flagp;
  1069. X{
  1070. X    register char  *ret;
  1071. X    register char  *chain;
  1072. X    register char  *latest;
  1073. X    int             flags;
  1074. X
  1075. X    *flagp = WORST;        /* Tentatively. */
  1076. X
  1077. X    ret = regnode(BRANCH);
  1078. X    chain = NULL;
  1079. X    while (*regparse != '\0' && *regparse != '|' && *regparse != ')') {
  1080. X    latest = regpiece(&flags);
  1081. X    if (latest == NULL)
  1082. X        return (NULL);
  1083. X    *flagp |= flags & HASWIDTH;
  1084. X    if (chain == NULL)    /* First piece. */
  1085. X        *flagp |= flags & SPSTART;
  1086. X    else
  1087. X        regtail(chain, latest);
  1088. X    chain = latest;
  1089. X    }
  1090. X    if (chain == NULL)        /* Loop ran zero times. */
  1091. X    regnode(NOTHING);
  1092. X
  1093. X    return (ret);
  1094. X}
  1095. X
  1096. X/*
  1097. X - regpiece - something followed by possible [*]
  1098. X *
  1099. X * Note that the branching code sequence used for * is somewhat optimized:
  1100. X * they use the same NOTHING node as both the endmarker for their branch
  1101. X * list and the body of the last branch.  It might seem that this node could
  1102. X * be dispensed with entirely, but the endmarker role is not redundant.
  1103. X */
  1104. Xstatic char *regpiece(flagp)
  1105. Xint            *flagp;
  1106. X{
  1107. X    register char  *ret;
  1108. X    register char   op;
  1109. X    register char  *nxt;
  1110. X    int             flags;
  1111. X
  1112. X    ret = regatom(&flags);
  1113. X    if (ret == NULL)
  1114. X    return (NULL);
  1115. X
  1116. X    op = *regparse;
  1117. X    if (!ISMULT(op)) {
  1118. X    *flagp = flags;
  1119. X    return (ret);
  1120. X    }
  1121. X    if (!(flags & HASWIDTH))
  1122. X    FAIL("* operand could be empty");
  1123. X    *flagp = (WORST | SPSTART);
  1124. X
  1125. X    if (op == '*' && (flags & SIMPLE))
  1126. X    reginsert(STAR, ret);
  1127. X    else if (op == '*') {
  1128. X    /* Emit x* as (x&|), where & means "self". */
  1129. X    reginsert(BRANCH, ret);    /* Either x */
  1130. X    regoptail(ret, regnode(BACK));    /* and loop */
  1131. X    regoptail(ret, ret);    /* back */
  1132. X    regtail(ret, regnode(BRANCH));    /* or */
  1133. X    regtail(ret, regnode(NOTHING));    /* null. */
  1134. X    }
  1135. X    regparse++;
  1136. X    if (ISMULT(*regparse))
  1137. X    FAIL("nested *");
  1138. X
  1139. X    return (ret);
  1140. X}
  1141. X
  1142. X/*
  1143. X - regatom - the lowest level
  1144. X *
  1145. X * Optimization:  gobbles an entire sequence of ordinary characters so that
  1146. X * it can turn them into a single node, which is smaller to store and
  1147. X * faster to run.  Backslashed characters are exceptions, each becoming a
  1148. X * separate node; the code is simpler that way and it's not worth fixing.
  1149. X */
  1150. Xstatic char *regatom(flagp)
  1151. Xint            *flagp;
  1152. X{
  1153. X    register char  *ret;
  1154. X    int             flags;
  1155. X
  1156. X    *flagp = WORST;        /* Tentatively. */
  1157. X
  1158. X    switch (*regparse++) {
  1159. X    case '^':
  1160. X    ret = regnode(BOL);
  1161. X    break;
  1162. X    case '$':
  1163. X    ret = regnode(EOL);
  1164. X    break;
  1165. X    case '.':
  1166. X    ret = regnode(ANY);
  1167. X    *flagp |= HASWIDTH | SIMPLE;
  1168. X    break;
  1169. X    case '[':{
  1170. X        register int    class;
  1171. X        register int    classend;
  1172. X
  1173. X        if (*regparse == '^') {    /* Complement of range. */
  1174. X        ret = regnode(ANYBUT);
  1175. X        regparse++;
  1176. X        } else
  1177. X        ret = regnode(ANYOF);
  1178. X        if (*regparse == ']' || *regparse == '-')
  1179. X        regc(*regparse++);
  1180. X        while (*regparse != '\0' && *regparse != ']') {
  1181. X        if (*regparse == '-') {
  1182. X            regparse++;
  1183. X            if (*regparse == ']' || *regparse == '\0')
  1184. X            regc('-');
  1185. X            else {
  1186. X            class = UCHARAT(regparse - 2) + 1;
  1187. X            classend = UCHARAT(regparse);
  1188. X            if (class > classend + 1)
  1189. X                FAIL("invalid [] range");
  1190. X            for (; class <= classend; class++)
  1191. X                regc(class);
  1192. X            regparse++;
  1193. X            }
  1194. X        } else
  1195. X            regc(*regparse++);
  1196. X        }
  1197. X        regc('\0');
  1198. X        if (*regparse != ']')
  1199. X        FAIL("unmatched []");
  1200. X        regparse++;
  1201. X        *flagp |= HASWIDTH | SIMPLE;
  1202. X    }
  1203. X    break;
  1204. X    case '(':
  1205. X    ret = reg(1, &flags);
  1206. X    if (ret == NULL)
  1207. X        return (NULL);
  1208. X    *flagp |= flags & (HASWIDTH | SPSTART);
  1209. X    break;
  1210. X    case '\0':
  1211. X    case '|':
  1212. X    case ')':
  1213. X    FAIL("internal urp");    /* Supposed to be caught earlier. */
  1214. X    break;
  1215. X    case '*':
  1216. X    FAIL("* follows nothing");
  1217. X    break;
  1218. X    case '\\':
  1219. X    if (*regparse == '\0')
  1220. X        FAIL("trailing \\");
  1221. X    ret = regnode(EXACTLY);
  1222. X    regc(*regparse++);
  1223. X    regc('\0');
  1224. X    *flagp |= HASWIDTH | SIMPLE;
  1225. X    break;
  1226. X    default:{
  1227. X        register int    len;
  1228. X        register char   ender;
  1229. X
  1230. X        regparse--;
  1231. X        len = strcspn(regparse, META);
  1232. X        if (len <= 0)
  1233. X        FAIL("internal disaster");
  1234. X        ender = *(regparse + len);
  1235. X        if (len > 1 && ISMULT(ender))
  1236. X        len--;        /* Back off clear of * operand. */
  1237. X        *flagp |= HASWIDTH;
  1238. X        if (len == 1)
  1239. X        *flagp |= SIMPLE;
  1240. X        ret = regnode(EXACTLY);
  1241. X        while (len > 0) {
  1242. X        regc(*regparse++);
  1243. X        len--;
  1244. X        }
  1245. X        regc('\0');
  1246. X    }
  1247. X    break;
  1248. X    }
  1249. X
  1250. X    return (ret);
  1251. X}
  1252. X
  1253. X/*
  1254. X - regnode - emit a node
  1255. X */
  1256. Xstatic char *regnode(op)
  1257. Xchar            op;
  1258. X{
  1259. X    register char  *ret;
  1260. X    register char  *ptr;
  1261. X
  1262. X    ret = regcode;
  1263. X    if (ret == ®dummy) {
  1264. X    regsize += 3;
  1265. X    return (ret);
  1266. X    }
  1267. X    ptr = ret;
  1268. X    *ptr++ = op;
  1269. X    *ptr++ = '\0';        /* Null "nxt" pointer. */
  1270. X    *ptr++ = '\0';
  1271. X    regcode = ptr;
  1272. X
  1273. X    return (ret);
  1274. X}
  1275. X
  1276. X/*
  1277. X - regc - emit (if appropriate) a byte of code
  1278. X */
  1279. Xstatic void regc(b)
  1280. Xchar            b;
  1281. X{
  1282. X    if (regcode != ®dummy)
  1283. X    *regcode++ = b;
  1284. X    else
  1285. X    regsize++;
  1286. X}
  1287. X
  1288. X/*
  1289. X - reginsert - insert an operator in front of already-emitted operand
  1290. X *
  1291. X * Means relocating the operand.
  1292. X */
  1293. Xstatic void reginsert(op, opnd)
  1294. Xchar            op;
  1295. Xchar           *opnd;
  1296. X{
  1297. X    register char  *src;
  1298. X    register char  *dst;
  1299. X    register char  *place;
  1300. X
  1301. X    if (regcode == ®dummy) {
  1302. X    regsize += 3;
  1303. X    return;
  1304. X    }
  1305. X    src = regcode;
  1306. X    regcode += 3;
  1307. X    dst = regcode;
  1308. X    while (src > opnd)
  1309. X    *--dst = *--src;
  1310. X
  1311. X    place = opnd;        /* Op node, where operand used to be. */
  1312. X    *place++ = op;
  1313. X    *place++ = '\0';
  1314. X    *place++ = '\0';
  1315. X}
  1316. X
  1317. X/*
  1318. X - regtail - set the next-pointer at the end of a node chain
  1319. X */
  1320. Xstatic void regtail(p, val)
  1321. Xchar           *p;
  1322. Xchar           *val;
  1323. X{
  1324. X    register char  *scan;
  1325. X    register char  *temp;
  1326. X    register int    offset;
  1327. X
  1328. X    if (p == ®dummy)
  1329. X    return;
  1330. X
  1331. X    /* Find last node. */
  1332. X    scan = p;
  1333. X    for (;;) {
  1334. X    temp = regnext(scan);
  1335. X    if (temp == NULL)
  1336. X        break;
  1337. X    scan = temp;
  1338. X    }
  1339. X
  1340. X    if (OP(scan) == BACK)
  1341. X    offset = scan - val;
  1342. X    else
  1343. X    offset = val - scan;
  1344. X    *(scan + 1) = (offset >> 8) & 0377;
  1345. X    *(scan + 2) = offset & 0377;
  1346. X}
  1347. X
  1348. X/*
  1349. X - regoptail - regtail on operand of first argument; nop if operandless
  1350. X */
  1351. Xstatic void regoptail(p, val)
  1352. Xchar           *p;
  1353. Xchar           *val;
  1354. X{
  1355. X    /* "Operandless" and "op != BRANCH" are synonymous in practice. */
  1356. X    if (p == NULL || p == ®dummy || OP(p) != BRANCH)
  1357. X    return;
  1358. X    regtail(OPERAND(p), val);
  1359. X}
  1360. X
  1361. X/*
  1362. X * regexec and friends
  1363. X */
  1364. X
  1365. X/*
  1366. X * Global work variables for regexec().
  1367. X */
  1368. Xstatic char    *reginput;    /* String-input pointer. */
  1369. Xstatic char    *regbol;        /* Beginning of input, for ^ check. */
  1370. Xstatic char   **regstartp;    /* Pointer to startp array. */
  1371. Xstatic char   **regendp;    /* Ditto for endp. */
  1372. X
  1373. X/*
  1374. X * Forwards.
  1375. X */
  1376. XSTATIC int      regtry();
  1377. XSTATIC int      regmatch();
  1378. XSTATIC int      regrepeat();
  1379. X
  1380. X#ifdef DEBUG
  1381. Xint             regnarrate = 0;
  1382. Xvoid            regdump();
  1383. XSTATIC char    *regprop();
  1384. X#endif
  1385. X
  1386. X/*
  1387. X - regexec - match a regexp against a string
  1388. X */
  1389. Xint regexec(prog, string)
  1390. Xregister regexp *prog;
  1391. Xregister char  *string;
  1392. X{
  1393. X    register char  *s;
  1394. X
  1395. X    /* Be paranoid... */
  1396. X    if (prog == NULL || string == NULL) {
  1397. X    regerror("NULL parameter");
  1398. X    return (0);
  1399. X    }
  1400. X    /* Check validity of program. */
  1401. X    if (UCHARAT(prog->program) != MAGIC) {
  1402. X    regerror("corrupted program");
  1403. X    return (0);
  1404. X    }
  1405. X    /* If there is a "must appear" string, look for it. */
  1406. X    if (prog->regmust != NULL) {
  1407. X    s = string;
  1408. X    while ((s = strchr(s, prog->regmust[0])) != NULL) {
  1409. X        if (strncmp(s, prog->regmust, prog->regmlen) == 0)
  1410. X        break;        /* Found it. */
  1411. X        s++;
  1412. X    }
  1413. X    if (s == NULL)        /* Not present. */
  1414. X        return (0);
  1415. X    }
  1416. X    /* Mark beginning of line for ^ . */
  1417. X    regbol = string;
  1418. X
  1419. X    /* Simplest case:  anchored match need be tried only once. */
  1420. X    if (prog->reganch)
  1421. X    return (regtry(prog, string));
  1422. X
  1423. X    /* Messy cases:  unanchored match. */
  1424. X    s = string;
  1425. X    if (prog->regstart != '\0')
  1426. X    /* We know what char it must start with. */
  1427. X    while ((s = strchr(s, prog->regstart)) != NULL) {
  1428. X        if (regtry(prog, s))
  1429. X        return (1);
  1430. X        s++;
  1431. X    }
  1432. X    else
  1433. X    /* We don't -- general case. */
  1434. X    do {
  1435. X        if (regtry(prog, s))
  1436. X        return (1);
  1437. X    } while (*s++ != '\0');
  1438. X
  1439. X    /* Failure. */
  1440. X    return (0);
  1441. X}
  1442. X
  1443. X/*
  1444. X - regtry - try match at specific point
  1445. X */
  1446. X#ifdef __STDC__
  1447. X
  1448. Xstatic int regtry(regexp *prog, char *string)
  1449. X
  1450. X#else
  1451. X
  1452. Xstatic int regtry(prog, string)
  1453. Xregexp         *prog;
  1454. Xchar           *string;
  1455. X
  1456. X#endif
  1457. X{
  1458. X    register int    i;
  1459. X    register char **sp;
  1460. X    register char **ep;
  1461. X
  1462. X    reginput = string;
  1463. X    regstartp = prog->startp;
  1464. X    regendp = prog->endp;
  1465. X
  1466. X    sp = prog->startp;
  1467. X    ep = prog->endp;
  1468. X    for (i = NSUBEXP; i > 0; i--) {
  1469. X    *sp++ = NULL;
  1470. X    *ep++ = NULL;
  1471. X    }
  1472. X    if (regmatch(prog->program + 1)) {
  1473. X    prog->startp[0] = string;
  1474. X    prog->endp[0] = reginput;
  1475. X    return (1);
  1476. X    } else
  1477. X    return (0);
  1478. X}
  1479. X
  1480. X/*
  1481. X - regmatch - main matching routine
  1482. X *
  1483. X * Conceptually the strategy is simple:  check to see whether the current
  1484. X * node matches, call self recursively to see whether the rest matches,
  1485. X * and then act accordingly.  In practice we make some effort to avoid
  1486. X * recursion, in particular by going through "ordinary" nodes (that don't
  1487. X * need to know whether the rest of the match failed) by a loop instead of
  1488. X * by recursion.
  1489. X */
  1490. X#ifdef __STDC__
  1491. X
  1492. Xstatic int regmatch(char *prog)
  1493. X
  1494. X#else
  1495. X
  1496. Xstatic int regmatch(prog)
  1497. Xchar           *prog;
  1498. X
  1499. X#endif
  1500. X{
  1501. X    register char  *scan;    /* Current node. */
  1502. X    char           *nxt;    /* nxt node. */
  1503. X
  1504. X    scan = prog;
  1505. X#ifdef DEBUG
  1506. X    if (scan != NULL && regnarrate)
  1507. X    fprintf(stderr, "%s(\n", regprop(scan));
  1508. X#endif
  1509. X    while (scan != NULL) {
  1510. X#ifdef DEBUG
  1511. X    if (regnarrate)
  1512. X        fprintf(stderr, "%s...\n", regprop(scan));
  1513. X#endif
  1514. X    nxt = regnext(scan);
  1515. X
  1516. X    switch (OP(scan)) {
  1517. X    case BOL:
  1518. X        if (reginput != regbol)
  1519. X        return (0);
  1520. X        break;
  1521. X    case EOL:
  1522. X        if (*reginput != '\0')
  1523. X        return (0);
  1524. X        break;
  1525. X    case ANY:
  1526. X        if (*reginput == '\0')
  1527. X        return (0);
  1528. X        reginput++;
  1529. X        break;
  1530. X    case EXACTLY:{
  1531. X        register int    len;
  1532. X        register char  *opnd;
  1533. X
  1534. X        opnd = OPERAND(scan);
  1535. X        /* Inline the first character, for speed. */
  1536. X        if (*opnd != *reginput)
  1537. X            return (0);
  1538. X        len = strlen(opnd);
  1539. X        if (len > 1 && strncmp(opnd, reginput, len) != 0)
  1540. X            return (0);
  1541. X        reginput += len;
  1542. X        }
  1543. X        break;
  1544. X    case ANYOF:
  1545. X        if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL)
  1546. X        return (0);
  1547. X        reginput++;
  1548. X        break;
  1549. X    case ANYBUT:
  1550. X        if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL)
  1551. X        return (0);
  1552. X        reginput++;
  1553. X        break;
  1554. X    case NOTHING:
  1555. X        break;
  1556. X    case BACK:
  1557. X        break;
  1558. X    case OPEN + 1:
  1559. X    case OPEN + 2:
  1560. X    case OPEN + 3:
  1561. X    case OPEN + 4:
  1562. X    case OPEN + 5:
  1563. X    case OPEN + 6:
  1564. X    case OPEN + 7:
  1565. X    case OPEN + 8:
  1566. X    case OPEN + 9:{
  1567. X        register int    no;
  1568. X        register char  *save;
  1569. X
  1570. X        no = OP(scan) - OPEN;
  1571. X        save = reginput;
  1572. X
  1573. X        if (regmatch(nxt)) {
  1574. X            /*
  1575. X             * Don't set startp if some later invocation of the same
  1576. X             * parentheses already has.
  1577. X             */
  1578. X            if (regstartp[no] == NULL)
  1579. X            regstartp[no] = save;
  1580. X            return (1);
  1581. X        } else
  1582. X            return (0);
  1583. X        }
  1584. X        break;
  1585. X    case CLOSE + 1:
  1586. X    case CLOSE + 2:
  1587. X    case CLOSE + 3:
  1588. X    case CLOSE + 4:
  1589. X    case CLOSE + 5:
  1590. X    case CLOSE + 6:
  1591. X    case CLOSE + 7:
  1592. X    case CLOSE + 8:
  1593. X    case CLOSE + 9:{
  1594. X        register int    no;
  1595. X        register char  *save;
  1596. X
  1597. X        no = OP(scan) - CLOSE;
  1598. X        save = reginput;
  1599. X
  1600. X        if (regmatch(nxt)) {
  1601. X            /*
  1602. X             * Don't set endp if some later invocation of the same
  1603. X             * parentheses already has.
  1604. X             */
  1605. X            if (regendp[no] == NULL)
  1606. X            regendp[no] = save;
  1607. X            return (1);
  1608. X        } else
  1609. X            return (0);
  1610. X        }
  1611. X        break;
  1612. X    case BRANCH:{
  1613. X        register char  *save;
  1614. X
  1615. X        if (OP(nxt) != BRANCH)    /* No choice. */
  1616. X            nxt = OPERAND(scan);    /* Avoid recursion. */
  1617. X        else {
  1618. X            do {
  1619. X            save = reginput;
  1620. X            if (regmatch(OPERAND(scan)))
  1621. X                return (1);
  1622. X            reginput = save;
  1623. X            scan = regnext(scan);
  1624. X            } while (scan != NULL && OP(scan) == BRANCH);
  1625. X            return (0);
  1626. X            /* NOTREACHED */
  1627. X        }
  1628. X        }
  1629. X        break;
  1630. X    case STAR:{
  1631. X        register char   nextch;
  1632. X        register int    no;
  1633. X        register char  *save;
  1634. X        register int    min;
  1635. X
  1636. X        /*
  1637. X         * Lookahead to avoid useless match attempts when we know
  1638. X         * what character comes next.
  1639. X         */
  1640. X        nextch = '\0';
  1641. X        if (OP(nxt) == EXACTLY)
  1642. X            nextch = *OPERAND(nxt);
  1643. X        min = (OP(scan) == STAR) ? 0 : 1;
  1644. X        save = reginput;
  1645. X        no = regrepeat(OPERAND(scan));
  1646. X        while (no >= min) {
  1647. X            /* If it could work, try it. */
  1648. X            if (nextch == '\0' || *reginput == nextch)
  1649. X            if (regmatch(nxt))
  1650. X                return (1);
  1651. X            /* Couldn't or didn't -- back up. */
  1652. X            no--;
  1653. X            reginput = save + no;
  1654. X        }
  1655. X        return (0);
  1656. X        }
  1657. X        break;
  1658. X    case END:
  1659. X        return (1);        /* Success! */
  1660. X        break;
  1661. X    default:
  1662. X        regerror("memory corruption");
  1663. X        return (0);
  1664. X        break;
  1665. X    }
  1666. X
  1667. X    scan = nxt;
  1668. X    }
  1669. X
  1670. X    /*
  1671. X     * We get here only if there's trouble -- normally "case END" is the
  1672. X     * terminating point.
  1673. X     */
  1674. X    regerror("corrupted pointers");
  1675. X    return (0);
  1676. X}
  1677. X
  1678. X/*
  1679. X - regrepeat - repeatedly match something simple, report how many
  1680. X */
  1681. X#ifdef __STDC__
  1682. X
  1683. Xstatic int regrepeat(char *p)
  1684. X
  1685. X#else
  1686. X
  1687. Xstatic int regrepeat(p)
  1688. Xchar           *p;
  1689. X
  1690. X#endif
  1691. X{
  1692. X    register int    count = 0;
  1693. X    register char  *scan;
  1694. X    register char  *opnd;
  1695. X
  1696. X    scan = reginput;
  1697. X    opnd = OPERAND(p);
  1698. X    switch (OP(p)) {
  1699. X    case ANY:
  1700. X    count = strlen(scan);
  1701. X    scan += count;
  1702. X    break;
  1703. X    case EXACTLY:
  1704. X    while (*opnd == *scan) {
  1705. X        count++;
  1706. X        scan++;
  1707. X    }
  1708. X    break;
  1709. X    case ANYOF:
  1710. X    while (*scan != '\0' && strchr(opnd, *scan) != NULL) {
  1711. X        count++;
  1712. X        scan++;
  1713. X    }
  1714. X    break;
  1715. X    case ANYBUT:
  1716. X    while (*scan != '\0' && strchr(opnd, *scan) == NULL) {
  1717. X        count++;
  1718. X        scan++;
  1719. X    }
  1720. X    break;
  1721. X    default:            /* Oh dear.  Called inappropriately. */
  1722. X    regerror("internal foulup");
  1723. X    count = 0;        /* Best compromise. */
  1724. X    break;
  1725. X    }
  1726. X    reginput = scan;
  1727. X
  1728. X    return (count);
  1729. X}
  1730. X
  1731. X
  1732. X/*
  1733. X - regnext - dig the "nxt" pointer out of a node
  1734. X */
  1735. X#ifdef __STDC__
  1736. X
  1737. Xstatic char *regnext(register char *p)
  1738. X
  1739. X#else
  1740. X
  1741. Xstatic char *regnext(p)
  1742. Xregister char  *p;
  1743. X
  1744. X#endif
  1745. X{
  1746. X    register int    offset;
  1747. X
  1748. X    if (p == ®dummy)
  1749. X    return (NULL);
  1750. X
  1751. X    offset = NEXT(p);
  1752. X    if (offset == 0)
  1753. X    return (NULL);
  1754. X
  1755. X    if (OP(p) == BACK)
  1756. X    return (p - offset);
  1757. X    else
  1758. X    return (p + offset);
  1759. X}
  1760. X
  1761. X#ifdef DEBUG
  1762. X
  1763. XSTATIC char    *regprop();
  1764. X
  1765. X/*
  1766. X - regdump - dump a regexp onto stdout in vaguely comprehensible form
  1767. X */
  1768. X#ifdef __STDC__
  1769. X
  1770. Xvoid regdump(regexp *r)
  1771. X
  1772. X#else
  1773. X
  1774. Xvoid regdump(r)
  1775. Xregexp         *r;
  1776. X
  1777. X#endif
  1778. X{
  1779. X    register char  *s;
  1780. X    register char   op = EXACTLY;    /* Arbitrary non-END op. */
  1781. X    register char  *nxt;
  1782. X    extern char    *strchr();
  1783. X
  1784. X
  1785. X    s = r->program + 1;
  1786. X    while (op != END) {        /* While that wasn't END last time... */
  1787. X    op = OP(s);
  1788. X    printf("%2d%s", s - r->program, regprop(s));    /* Where, what. */
  1789. X    nxt = regnext(s);
  1790. X    if (nxt == NULL)    /* nxt ptr. */
  1791. X        printf("(0)");
  1792. X    else
  1793. X        printf("(%d)", (s - r->program) + (nxt - s));
  1794. X    s += 3;
  1795. X    if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
  1796. X        /* Literal string, where present. */
  1797. X        while (*s != '\0') {
  1798. X        putchar(*s);
  1799. X        s++;
  1800. X        }
  1801. X        s++;
  1802. X    }
  1803. X    putchar('\n');
  1804. X    }
  1805. X
  1806. X    /* Header fields of interest. */
  1807. X    if (r->regstart != '\0')
  1808. X    printf("start `%c' ", r->regstart);
  1809. X    if (r->reganch)
  1810. X    printf("anchored ");
  1811. X    if (r->regmust != NULL)
  1812. X    printf("must have \"%s\"", r->regmust);
  1813. X    printf("\n");
  1814. X}
  1815. X
  1816. X/*
  1817. X - regprop - printable representation of opcode
  1818. X */
  1819. X#ifdef __STDC__
  1820. X
  1821. Xstatic char *regprop(char *op)
  1822. X
  1823. X#else
  1824. X
  1825. Xstatic char *regprop(op)
  1826. Xchar           *op;
  1827. X
  1828. X#endif
  1829. X{
  1830. X    register char  *p;
  1831. X    static char     buf[50];
  1832. X
  1833. X    strcpy(buf, ":");
  1834. X
  1835. X    switch (OP(op)) {
  1836. X    case BOL:
  1837. X    p = "BOL";
  1838. X    break;
  1839. X    case EOL:
  1840. X    p = "EOL";
  1841. X    break;
  1842. X    case ANY:
  1843. X    p = "ANY";
  1844. X    break;
  1845. X    case ANYOF:
  1846. X    p = "ANYOF";
  1847. X    break;
  1848. X    case ANYBUT:
  1849. X    p = "ANYBUT";
  1850. X    break;
  1851. X    case BRANCH:
  1852. X    p = "BRANCH";
  1853. X    break;
  1854. X    case EXACTLY:
  1855. X    p = "EXACTLY";
  1856. X    break;
  1857. X    case NOTHING:
  1858. X    p = "NOTHING";
  1859. X    break;
  1860. X    case BACK:
  1861. X    p = "BACK";
  1862. X    break;
  1863. X    case END:
  1864. X    p = "END";
  1865. X    break;
  1866. X    case OPEN + 1:
  1867. X    case OPEN + 2:
  1868. X    case OPEN + 3:
  1869. X    case OPEN + 4:
  1870. X    case OPEN + 5:
  1871. X    case OPEN + 6:
  1872. X    case OPEN + 7:
  1873. X    case OPEN + 8:
  1874. X    case OPEN + 9:
  1875. X    sprintf(buf + strlen(buf), "OPEN%d", OP(op) - OPEN);
  1876. X    p = NULL;
  1877. X    break;
  1878. X    case CLOSE + 1:
  1879. X    case CLOSE + 2:
  1880. X    case CLOSE + 3:
  1881. X    case CLOSE + 4:
  1882. X    case CLOSE + 5:
  1883. X    case CLOSE + 6:
  1884. X    case CLOSE + 7:
  1885. X    case CLOSE + 8:
  1886. X    case CLOSE + 9:
  1887. X    sprintf(buf + strlen(buf), "CLOSE%d", OP(op) - CLOSE);
  1888. X    p = NULL;
  1889. X    break;
  1890. X    case STAR:
  1891. X    p = "STAR";
  1892. X    break;
  1893. X    default:
  1894. X    regerror("corrupted opcode");
  1895. X    break;
  1896. X    }
  1897. X    if (p != NULL)
  1898. X    strcat(buf, p);
  1899. X    return (buf);
  1900. X}
  1901. X#endif
  1902. X
  1903. X/*
  1904. X * The following is provided for those people who do not have strcspn() in
  1905. X * their C libraries.  They should get off their butts and do something
  1906. X * about it; at least one public-domain implementation of those (highly
  1907. X * useful) string routines has been published on Usenet.
  1908. X */
  1909. X#ifdef STRCSPN
  1910. X/*
  1911. X * strcspn - find length of initial segment of s1 consisting entirely
  1912. X * of characters not from s2
  1913. X */
  1914. X
  1915. X#ifdef __STDC__
  1916. X
  1917. Xstatic int strcspn(char *s1, char *s2)
  1918. X
  1919. X#else
  1920. X
  1921. Xstatic int strcspn(s1, s2)
  1922. Xchar           *s1;
  1923. Xchar           *s2;
  1924. X
  1925. X#endif
  1926. X{
  1927. X    register char  *scan1;
  1928. X    register char  *scan2;
  1929. X    register int    count;
  1930. X
  1931. X    count = 0;
  1932. X    for (scan1 = s1; *scan1 != '\0'; scan1++) {
  1933. X    for (scan2 = s2; *scan2 != '\0';)    /* ++ moved down. */
  1934. X        if (*scan1 == *scan2++)
  1935. X        return (count);
  1936. X    count++;
  1937. X    }
  1938. X    return (count);
  1939. X}
  1940. X#endif
  1941. X
  1942. X
  1943. X/*
  1944. X - regsub - perform substitutions after a regexp match
  1945. X */
  1946. X#ifdef __STDC__
  1947. X
  1948. Xvoid regsub(regexp *prog, char *source, char *dest)
  1949. X
  1950. X#else
  1951. X
  1952. Xvoid regsub(prog, source, dest)
  1953. Xregexp         *prog;
  1954. Xchar           *source;
  1955. Xchar           *dest;
  1956. X
  1957. X#endif
  1958. X{
  1959. X    register char  *src;
  1960. X    register char  *dst;
  1961. X    register char   c;
  1962. X    register int    no;
  1963. X    register int    len;
  1964. X    extern char    *strncpy();
  1965. X
  1966. X    if (prog == NULL || source == NULL || dest == NULL) {
  1967. X    regerror("NULL parm to regsub");
  1968. X    return;
  1969. X    }
  1970. X    if (UCHARAT(prog->program) != MAGIC) {
  1971. X    regerror("damaged regexp fed to regsub");
  1972. X    return;
  1973. X    }
  1974. X    src = source;
  1975. X    dst = dest;
  1976. X    while ((c = *src++) != '\0') {
  1977. X    if (c == '&')
  1978. X        no = 0;
  1979. X    else if (c == '\\' && '0' <= *src && *src <= '9')
  1980. X        no = *src++ - '0';
  1981. X    else
  1982. X        no = -1;
  1983. X
  1984. X    if (no < 0) {        /* Ordinary character. */
  1985. X        if (c == '\\' && (*src == '\\' || *src == '&'))
  1986. X        c = *src++;
  1987. X        *dst++ = c;
  1988. X    } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) {
  1989. X        len = prog->endp[no] - prog->startp[no];
  1990. X        strncpy(dst, prog->startp[no], len);
  1991. X        dst += len;
  1992. X        if (len != 0 && *(dst - 1) == '\0') {    /* strncpy hit NUL. */
  1993. X        regerror("damaged match string");
  1994. X        return;
  1995. X        }
  1996. X    }
  1997. X    }
  1998. X    *dst++ = '\0';
  1999. X}
  2000. X
  2001. X
  2002. X#ifdef __STDC__
  2003. X
  2004. Xvoid regerror(char *s)
  2005. X
  2006. X#else
  2007. X
  2008. Xvoid regerror(s)
  2009. Xchar           *s;
  2010. X
  2011. X#endif
  2012. X{
  2013. X#ifdef NN
  2014. X    msg("REGEXP ERROR: %s", s);
  2015. X#else
  2016. X    fprintf(stderr, "regexp(3): %s", s);
  2017. X    exit(1);
  2018. X#endif
  2019. X}
  2020. END_OF_FILE
  2021.   if test 30796 -ne `wc -c <'regexp.c'`; then
  2022.     echo shar: \"'regexp.c'\" unpacked with wrong size!
  2023.   fi
  2024.   # end of 'regexp.c'
  2025. fi
  2026. echo shar: End of archive 8 \(of 22\).
  2027. cp /dev/null ark8isdone
  2028. MISSING=""
  2029. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ; do
  2030.     if test ! -f ark${I}isdone ; then
  2031.     MISSING="${MISSING} ${I}"
  2032.     fi
  2033. done
  2034. if test "${MISSING}" = "" ; then
  2035.     echo You have unpacked all 22 archives.
  2036.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  2037. else
  2038.     echo You still must unpack the following archives:
  2039.     echo "        " ${MISSING}
  2040. fi
  2041. exit 0
  2042.  
  2043. exit 0 # Just in case...
  2044.